package replicamanager;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;

import communication.*;
public class GroupManager {

	private static int replicaManagerPort=60500;
	private static HashMap<Integer,String> replicaInfoTable;
	RMMessage message;
	String serverName1;
	String serverName2;
	String serverName3;
	InetAddress address1;
	InetAddress address2;
	InetAddress address3;
	int port1=61100;
	int port2=61101;
	int port3=61102;

	public GroupManager(){
		try
		{

			replicaInfoTable= new HashMap<Integer, String>();
			serverName1= "cherokee";
			serverName2= "comanche";
			serverName3= "localhost";
			address1 = InetAddress.getByName(serverName1);
			address2 = InetAddress.getByName(serverName2);
			address3 = InetAddress.getByName(serverName3);
		}
		catch (UnknownHostException e)
		{
			System.out.println("Host Unreachable !");
			e.printStackTrace();
		}


	}
	public HashMap<Integer, String> getReplicaInfoTable() {
		return replicaInfoTable;
	}

	private void setTable()	{
		replicaInfoTable.put(port1, serverName1);
		replicaInfoTable.put(port2, serverName2);
		replicaInfoTable.put(port3, serverName3);
	}

	public int recieveAndSend( int rmID){
		MRMessage request = new MRMessage();
		if(rmID == 1){
			DatagramSocket clientSocket =null;
			try{
				clientSocket = new DatagramSocket();
				DatagramPacket sendPacket;
				byte[] sendData = new byte[1024];
				byte[] receiveData = new byte[1024];


				String message=request.getPacket();
				sendData=message.getBytes();
				sendPacket= new DatagramPacket(sendData, sendData.length,address3,port3);
				clientSocket.send(sendPacket);
				DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
				clientSocket.receive(receivePacket);
				System.out.println("received data"+ receiveData.toString());
				// String state = new String(receivePacket.getData()).trim();
				//sendPacket = new DatagramPacket receivePacket();
				sendData = receiveData;
				boolean check = true;
				while(check)
				{
					sendPacket= new DatagramPacket(sendData, sendData.length,address1,port1);
					clientSocket.send(sendPacket);

					receivePacket = new DatagramPacket(receiveData, receiveData.length);
					clientSocket.receive(receivePacket);
					if(receiveData.toString()=="1")
					{
						check= false;
						System.out.println("Update done");
						clientSocket.close();
						return 1;
					}
				}

				System.out.println("updating done");

			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		else if(rmID ==2){

			DatagramSocket clientSocket =null;
			try{
				clientSocket = new DatagramSocket();
				DatagramPacket sendPacket;
				byte[] sendData = new byte[1024];
				byte[] receiveData = new byte[1024];

				//		int GetStateAddress= InetAddress.getByName(GetStateAddr);
				String message=request.getPacket();
				sendData=message.getBytes();
				sendPacket= new DatagramPacket(sendData, sendData.length,address3,port3);
				clientSocket.send(sendPacket);
				DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
				clientSocket.receive(receivePacket);
				// String state = new String(receivePacket.getData()).trim();
				//sendPacket = new DatagramPacket receivePacket();
				sendData = receiveData;
				boolean check = true;
				while(check)
				{
					sendPacket= new DatagramPacket(sendData, sendData.length,address2,port2);
					clientSocket.send(sendPacket);

					receivePacket = new DatagramPacket(receiveData, receiveData.length);
					clientSocket.receive(receivePacket);
					if(receiveData.toString()=="1")
					{
						check= false;
						System.out.println("Update done");
						clientSocket.close();
						return 1;
					}
				}
				clientSocket.close();

			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
		else if(rmID == 3){
			DatagramSocket clientSocket =null;
			try{
				clientSocket = new DatagramSocket();
				DatagramPacket sendPacket;
				byte[] sendData = new byte[1024];
				byte[] receiveData = new byte[1024];
				//sendstop(int 3)
				//		int GetStateAddress= InetAddress.getByName(GetStateAddr);
				String message=request.getPacket();
				sendData=message.getBytes();
				sendPacket= new DatagramPacket(sendData, sendData.length,address1,port1);
				clientSocket.send(sendPacket);
				DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
				clientSocket.receive(receivePacket);
				// String state = new String(receivePacket.getData()).trim();
				//sendPacket = new DatagramPacket receivePacket();
				sendData = receiveData;
				Message m = new Message();
				boolean check = true;
				while(check)
				{
					sendPacket= new DatagramPacket(sendData, sendData.length,address3,port3);
					clientSocket.send(sendPacket);
					//recieve reply from server

					receivePacket = new DatagramPacket(receiveData, receiveData.length);
					clientSocket.receive(receivePacket);
					int receivedRM = m.byteToInt(receiveData);
					if(receivedRM==1)
					{
						check= false;
						System.out.println("Update done");
						clientSocket.close();
						return 1;
					}
				}    

			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return 0; 

	}


	public void sendStop(int rm){
		int port = 0;
		InetAddress address = null;
		if (rm == 1)
		{
			port = port1; 
			address = address1;
		}
		else if( rm== 2)
		{
			port = port2; 
			address = address2;
		}
		else if( rm== 3)
		{
			port = port3; 
			address = address3;
		}

		//
		DatagramSocket clientSocket =null;
		{
			try {
				clientSocket = new DatagramSocket();

				DatagramPacket sendPacket;
				byte[] sendData = new byte[1024];
				byte[] receiveData = new byte[1024];
				String message="Stop";
				sendData=message.getBytes();
				sendPacket= new DatagramPacket(sendData, sendData.length,address,port);
				try {
					clientSocket.send(sendPacket);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			} 
			catch (SocketException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}  
		}
	}

	public void receiveFault(){

		System.out.println("Replica Listener active...\n");
		int faultReceived = 0;
		{
			DatagramSocket serverSocket;
			try {
				serverSocket = new DatagramSocket(replicaManagerPort);

				byte[] receiveData = new byte[1024]; 
				byte[] sendData = new byte[1024];

				while(true) 
				{
					System.out.println("Waiting for new request...");
					DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
					try {
						serverSocket.receive(receivePacket);
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					String receivedMessage = new String(receivePacket.getData()).trim();
					System.out.println("Received: " + receivedMessage); 
					InetAddress IPAddress = receivePacket.getAddress();
					int port = receivePacket.getPort();
					int faultyReplica = Integer.parseInt(receivedMessage);

					if(faultReceived != faultyReplica){
						faultReceived = faultyReplica;
						int msg = recieveAndSend(faultyReplica);

						DatagramPacket sendPacket;
						sendData =  Integer.toString(msg).getBytes();

						sendPacket= new DatagramPacket(sendData, sendData.length, IPAddress, port);
						int count = 0;
						do {
							try {
								serverSocket.send(sendPacket);
								count++;
							} catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}while(count<3);
						// end of processing fe request
					} else{
						// repeated request, so ignore
					}
				}
			} catch (SocketException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}

	}

	public static void main (String[] args)	{
		GroupManager gm = new GroupManager();
		gm.setTable();
		gm.printGroup();
		gm.receiveFault();
	}

	private void printGroup() {

		System.out.println("\nGroup information:");
		System.out.println("------------------");
		/*Iterator<Entry<Integer, String>> iterator = replicaInfoTable.entrySet().iterator();   

		while (iterator.hasNext()) {   
			String key = iterator.next().toString();   
			String value = replicaInfoTable.get(key);   

			System.out.println(key + " " + value);
		}  */
		System.out.println("Replica 1 - Rabe's implementation in "+replicaInfoTable.get(61100) + " listening at port "+port1+".");
		System.out.println("Replica 2 - Navjot's implementation in "+replicaInfoTable.get(61101) + " listening at port "+port2+".");
		System.out.println("Replica 3 - Abirami's implementation in "+replicaInfoTable.get(61102) + " listening at port "+port3+".");
		System.out.println();
	}
}